#ifndef __BLOCK_HPP__
#define __BLOCK_HPP__

#include <string>
#include <cstdint>
#include <vector>
#include <set>
#include "Engine/Random.hpp"
#include "Game/CellFace.h"
#include "Game/BlockDropValue.hpp"
#include "Game/BlockPlacementData.hpp"
#include "Game/TerrainRaycastResult.hpp"
#include "Terrain/Terrain.h"
#include "BlockConstans.h"
#include "Utils/Hash.hpp"
#include "Engine/Point3.hpp"

using namespace PocketSurvival::Engine;
namespace PocketSurvival
{
    struct ComponentMiner;

    enum class BlockDigMethod
    {
        None,
        Shovel,
        Quarry,
        Hack
    };

    enum class BlockFoodType
    {
		None,
		Meat,
		Fish,
		Fruit,
		Grass,
		Bread
    };

    class Block
    {
    protected:
        Random m_random;
        std::set<size_t> m_inheritanceSet;
    public:
        std::string     DefaultDisplayName;                 // 方块名称, 游戏中玩家看到的物品名字
        std::string     DefaultCategory;                    // 方块类别, 帮助中的类别
        std::string     Behaviors;                          // 方块行为, 即方块的特殊属性，多个行为可以用逗号隔开
        int32_t         DisplayOrder;                       // 显示顺序, 游戏中创造模式物品栏中物品的显示顺序(同时是这个方块在该文件中所在的行数)
        std::string     DefaultIconBlockOffset;             // 图标偏移(2), 物品栏内方向偏移
        std::string     DefaultIconViewOffset;              // 图标偏移(3), 物品栏内3D方向偏移
        std::string     DefaultIconViewScale;               // 图标大小, 在物品栏里显示的大小
        std::string     FirstPersonScale;                   // 第一人称缩放, 第一人称视角下手持物品的大小缩放倍数
        std::string     FirstPersonOffset;                  // 第一人称偏移, 
        std::string     FirstPersonRotation;                // 第一人称旋转, 
        std::string     InHandScale;                        // 手握缩放,
        std::string     InHandOffset;                       // 手握偏移,
        std::string     InHandRotation;                     // 手握旋转,
        std::string     CraftingId;                         // 合成标识,
        int32_t         DefaultCreativeData;                // 创造提供(data数据),
        bool            IsCollidable;                       // 是否可碰撞
        bool            IsPlaceable;                        // 是否可放置,
        bool            IsDiggingTransparent;               // 破坏透明,
        bool            IsPlacementTransparent;             // 放置透明
        bool            DefaultIsInteractive;               // 是否交互
        bool            IsEditable;                         // 是否可编辑
        bool            IsNonDuplicable;                    // 是否不可复制
        bool            IsGatherable;                       // 是否可集中
        bool            HasCollisionBehavior;               // 碰撞行为
        bool            KillsWhenStuck;                     // 卡住时杀死
        bool            IsFluidBlocker;                     // 能否阻挡流体
        bool            IsTransparent;                      // 是否透明
        int32_t         DefaultShadowStrength;              // 阴影强度(1-15)
        int32_t         LightAttenuation;                   // 光衰减
        int32_t         DefaultEmittedLightAmount;          // 发光强度(0-23)
        float           ObjectShadowStrength;               // 物体阴影强度
        int32_t         DefaultDropContent;                 // 掉落物品
        float           DefaultDropCount;                   // 掉落数量
        float           DefaultExperienceCount;             // 掉落经验
        int32_t         RequiredToolLevel;                  // 需求工具等级
        int32_t         MaxStacking;                        // 最大背包堆叠
        float           SleepSuitability;                   // 适合睡觉程度
        float           FrictionFactor;                     // 摩擦系数
        float           Density;                            // 物品重量
        bool            NoAutoJump;                         // 阻挡跳跃
        bool            NoSmoothRise;                       // 阻挡动物
        float           FuelHeatLevel;                      // 燃料等级
        float           FuelFireDuration;                   // 炉中燃料时间
        std::string     DefaultSoundMaterialName;           // 声音材料名称
        float           ShovelPower;                        // 铲子效率
        float           QuarryPower;                        // 镐子效率
        float           HackPower;                          // 斧头效率
        float           DefaultMeleePower;                  // 近战伤害
        float           DefaultMeleeHitProbability;         // 近战命中率
        float           DefaultProjectilePower;             // 投掷伤害
        int32_t         ToolLevel;                          // 工具等级
        int32_t         PlayerLevelRequired;                // 玩家级别需要
        int32_t         Durability;                         // 工具耐久度
        bool            IsAimable;                          // 是否可瞄准
        bool            IsStickable;                        // 击中是否停留
        bool            AlignToVelocity;                    // 成直线速率
        float           ProjectileSpeed;                    // 投掷速度
        float           ProjectileDamping;                  // 投掷阻力
        float           ProjectileTipOffset;                // 扔出点偏移
        bool            DisintegratesOnHit;                 // 撞击时碎裂
        float           ProjectileStickProbability;         // 粘性可能性
        float           DefaultHeat;                        // 默认热量
        float           FireDuration;                       // 露天燃烧时间
        float           ExplosionResilience;                // 爆炸抗性
        float           DefaultExplosionPressure;           // 爆炸压力
        bool            DefaultExplosionIncendiary;         // 爆炸燃烧
        bool            IsExplosionTransparent;             // 爆炸透明
        BlockDigMethod  DigMethod;                          // 挖掘方法
        float           DigResilience;                      // 挖掘抗性
        float           ProjectileResilience;               // 弹射物抗性
        float           DefaultNutritionalValue;            // 营养价值
        float           DefaultSicknessProbability;         // 疾病概率
        BlockFoodType   FoodType;                           // 食物类型
        int32_t         DefaultRotPeriod;                   // 食物腐烂周期
        std::string     DefaultTextureSlot;                 // 材质位置
        std::string     DestructionDebrisScale;             // 破坏碎片大小
        
        
    public:
        virtual int32_t getIndex() = 0;
        virtual const std::string &getBlockName() = 0;

        template<typename Type>
        constexpr inline void addInheritance(const Type& type)
        {
            size_t hashValue = Utils::Hash::GetTypeHash<Type>();
            m_inheritanceSet.insert(hashValue);
        }

        template<typename Type>
        inline bool isBlock()
        {
            if(this == nullptr)
            {
                return false;
            }
            size_t hashValue = Utils::Hash::GetTypeHash<Type>();
            if(m_inheritanceSet.find(hashValue) != m_inheritanceSet.end())
            {
                return true;
            }
            return false;
        }
        
        template<typename Type>
        inline Type asBlock()
        {
            if(this != nullptr && isBlock<Type>() == true)
            {
                return static_cast<Type>(this);
            }
            return nullptr;
        }

        inline virtual void initialize();
        inline virtual float getDensity(int32_t value);
        inline virtual float getExplosionPressure(int32_t value);
        inline virtual bool getExplosionIncendiary(int32_t value);
        inline virtual int32_t getMaxStacking(int32_t value);
        inline virtual bool matchCrafingId(const std::string &CraftId);
        inline virtual std::string getCategory(int32_t value);
        inline virtual float getDigResilience(int32_t value);
        inline virtual BlockPlacementData getDigValue(ComponentMiner *componentMiner, int32_t value, int32_t toolValue,const TerrainRaycastResult &raycastResult);
        inline virtual int32_t getDamage(int32_t value);
        inline virtual int32_t setDamage(int32_t value, int32_t damage);
        inline virtual int32_t getDamageDestructionValue(int32_t value);
        inline virtual void getDropValues(int32_t oldValue, int32_t newValue, int32_t toolLevel, std::vector<BlockDropValue> &dropValues);
        inline virtual void getCreativeValues(std::vector<int32_t> &blockVec);
        inline virtual BlockPlacementData getPlacementValue(ComponentMiner *componentMiner, int32_t value, const TerrainRaycastResult &raycastResult);
        inline virtual const std::vector<BoundingBox> &getCustomCollisionBoxes(int32_t value);
        inline virtual const std::vector<BoundingBox> &getCustomInteractionBoxes(int32_t value);
        inline virtual bool getIsCollidable(int32_t value);
        inline virtual bool isHeatBlocker(int32_t value);
        inline virtual float getNutritionalValue(int32_t value);

        // 食物吃完剩下的物品
        inline virtual void getEatRamins(int32_t &outValue, uint32_t &outCount);

        inline virtual float getSicknessProbability(int32_t value);
        inline virtual float getProjectilePower(int32_t value);
        inline virtual bool isFaceTransparent(int32_t face, int32_t value);
        inline virtual bool isTransparent(int32_t value);

        inline virtual int32_t getEmittedLightAmount(int32_t value);
        inline virtual bool shouldAvoid(int32_t value);                 // 应该避免?
        inline virtual int32_t getRotPeriod(int32_t value);
        inline virtual float getMeleePower(int32_t value);
        inline virtual float getMeleeHitProbability(int32_t value);
        inline virtual bool isInteractive(int32_t value);
        inline virtual float getHeat(int32_t value);
        // inline virtual std::vector<int32_t> getCreativeValues();


        inline virtual BlockDigMethod getBlockDigMethod(int32_t value);

        inline std::shared_ptr<float> Raycast(const Ray3 &ray, int32_t value, bool useInteractionBoxes, int32_t &outNearestBoxIndex, BoundingBox &outNearestBox);
    };

    inline void Block::initialize() {}

    inline float Block::getDensity(int32_t value)
    {
        return Density;
    }
    inline float Block::getExplosionPressure(int32_t value)
    {
        return DefaultExplosionPressure;
    }
    inline bool Block::getExplosionIncendiary(int32_t value)
    {
        return DefaultExplosionIncendiary;
    }
    inline int32_t Block::getMaxStacking(int32_t value)
    {
        return MaxStacking;
    }
    inline bool Block::matchCrafingId(const std::string &CraftId)
    {
        return CraftId == CraftingId;
    }
    inline std::string Block::getCategory(int32_t value)
    {
        return DefaultCategory;
    }
    inline BlockDigMethod Block::getBlockDigMethod(int32_t value)
    {
        return DigMethod;
    }
    inline float Block::getDigResilience(int32_t value)
    {
        return DigResilience;
    }
    inline BlockPlacementData Block::getDigValue(ComponentMiner *componentMiner, int32_t value, int32_t toolValue,const TerrainRaycastResult &raycastResult)
    {
        BlockPlacementData result;
        result.value = 0;
        result.cellFace = raycastResult.cellFace;
        return result;
    }
    inline int32_t Block::getDamage(int32_t value)
    {
        return (Terrain::ExtractData(value) >> 4) & 0xFFF;
    }
    inline int32_t Block::setDamage(int32_t value, int32_t damage)
    {
        int32_t num = Terrain::ExtractData(value);
        num &= 0xF;
        num |= MathUtils::Clamp<int32_t>(damage, 0, 4095) << 4;
        return Terrain::ReplaceData(value, num);
    }
    inline int32_t Block::getDamageDestructionValue(int32_t value)
    {
        return 0;
    }
    inline void Block::getDropValues(int32_t oldValue, int32_t newValue, int32_t toolLevel, std::vector<BlockDropValue> &dropValues)
    {
        if(toolLevel < RequiredToolLevel)
        {
            return;
        }
        if(DefaultDropContent != 0)
        {
            int32_t num = (int32_t)DefaultDropCount;
            if(m_random.getBool(DefaultDropCount - num))
            {
                num++;
            }
            for(int32_t i = 0; i < num; i++)
            {
                dropValues.push_back(BlockDropValue{ Terrain::MakeBlockValue(DefaultDropContent), 1});
            }
        }
        int32_t num2 = (int32_t)DefaultExperienceCount;
        if(m_random.getBool(DefaultExperienceCount - num2))
        {
            num2++;
        }
        for (int32_t i = 0; i < num2; i++)
        {
            dropValues.push_back(BlockDropValue{ Terrain::MakeBlockValue(248), 1});
        }
    }
    inline void Block::getCreativeValues(std::vector<int32_t> &blockVec)
    {
        if(DefaultCreativeData >= 0)
        {
            blockVec.push_back(Terrain::ReplaceContents(Terrain::ReplaceData(0, DefaultCreativeData), getIndex()));
        }
    }
    inline BlockPlacementData Block::getPlacementValue(ComponentMiner *componentMiner, int32_t value, const TerrainRaycastResult &raycastResult)
    {
        BlockPlacementData result;
        result.value = value;
        result.cellFace = raycastResult.cellFace;
        return result;
    }
    inline const std::vector<BoundingBox> &Block::getCustomCollisionBoxes(int32_t value)
    {
        return BlockConstans::defaultCollisionBoxes;
    }
    inline const std::vector<BoundingBox> &Block::getCustomInteractionBoxes(int32_t value)
    {
        return getCustomCollisionBoxes(value);
    }
    inline bool Block::getIsCollidable(int32_t value)
    {
        return IsCollidable;
    }
    inline bool Block::isHeatBlocker(int32_t value)
    {
        return getIsCollidable(value);
    }
    inline std::shared_ptr<float> Block::Raycast(const Ray3 &ray, int32_t value, bool useInteractionBoxes, int32_t &outNearestBoxIndex, BoundingBox &outNearestBox)
    {
        std::shared_ptr<float> result = nullptr;
        outNearestBoxIndex = 0;
        const std::vector<BoundingBox> &array = useInteractionBoxes ? getCustomCollisionBoxes(value) : getCustomCollisionBoxes(value);
        for(int32_t index = 0; index < array.size(); ++index)
        {
            const BoundingBox& box = array[index];
            float num;
            if(ray.Intersection(box, num) == true && (result == nullptr || num < *result))
            {
                outNearestBoxIndex = index;
                result = std::make_shared<float>(num);
            }
        }
        outNearestBox = array[outNearestBoxIndex];
        return result;
    }
    inline float Block::getNutritionalValue(int32_t value)
    {
        return DefaultNutritionalValue;
    }
    inline void Block::getEatRamins(int32_t &outValue, uint32_t &outCount)
    {
        outValue = 0;
        outCount = 0;
    }

    inline float Block::getSicknessProbability(int32_t value)
    {
        return DefaultSicknessProbability;
    }
    inline float Block::getProjectilePower(int32_t value)
    {
        return DefaultProjectilePower;
    }
    inline bool Block::isFaceTransparent(int32_t face, int32_t value)
    {
        return IsTransparent;
    }
    inline bool Block::isTransparent(int32_t value)
    {
        return IsTransparent;
    }

    inline int32_t Block::getEmittedLightAmount(int32_t value)
    {
        return DefaultEmittedLightAmount;
    }
    inline bool Block::shouldAvoid(int32_t value)
    {
        return false;
    }
    inline int32_t Block::getRotPeriod(int32_t value)
    {
        return DefaultRotPeriod;
    }
    inline float Block::getMeleePower(int32_t value)
    {
        return DefaultMeleePower;
    }
    inline float Block::getMeleeHitProbability(int32_t value)
    {
        return DefaultMeleeHitProbability;
    }
    inline bool Block::isInteractive(int32_t value)
    {
        return DefaultIsInteractive;
    }
    inline float Block::getHeat(int32_t value)
    {
        return DefaultHeat;
    }


}

#endif